//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
{{>licenseInfo}}


// generated from apiService.mustache
package {{apiPackage}}

{{#imports}}import _root_.{{import}}
{{/imports}}
import scala.util.Failure
import scala.util.Try
import _root_.{{modelPackage}}.*

/**
 * The {{classname}}Service companion object.
 *
 * Use the {{classname}}Service() companion object to create an instance which returns a 'not implemented' error
 * for each operation.
 *
 */
object {{classname}}Service {

  /**
   * The 'Handler' is an implementation of {{classname}}Service convenient for delegating or overriding individual functions
   */
  case class Handler[F[_]](
{{#operations}}
    {{#operation}}
        {{operationId}}Handler : ({{{vendorExtensions.x-param-list-typed}}}) => F[{{{vendorExtensions.x-response-type}}}]{{^-last}}, {{/-last}}
    {{/operation}}
{{/operations}}
  ) extends {{classname}}Service[F] {
{{#operations}}
    {{#operation}}

        override def {{operationId}}({{{vendorExtensions.x-param-list-typed}}}) : F[{{{vendorExtensions.x-response-type}}}] = {
          {{operationId}}Handler({{{vendorExtensions.x-param-list}}})
        }
    {{/operation}}
{{/operations}}
  }

  def apply() : {{classname}}Service[Try] = {{classname}}Service.Handler[Try](
{{#operations}}
    {{#operation}}
        ({{#allParams}}_{{^-last}}, {{/-last}}{{/allParams}}) => notImplemented("{{operationId}}"){{^-last}}, {{/-last}}
    {{/operation}}
{{/operations}}
  )

  private def notImplemented(name : String) = Failure(new Exception(s"TODO: $name not implemented"))
}

/**
 * The {{classname}} business-logic
 *
 *
 * The 'asHandler' will return an implementation which allows for easily overriding individual operations.
 *
 * equally there are "on&lt;Function&gt;" helper methods for easily overriding individual functions
 *
 * @tparam F the effect type (Future, Try, IO, ID, etc) of the operations
 */
trait {{classname}}Service[F[_]] {
{{#operations}}
{{#operation}}
  /** {{{summary}}}
   * {{{description}}}
   * @return {{returnType}}
   */
  def {{operationId}}({{{vendorExtensions.x-param-list-typed}}}) : F[{{{vendorExtensions.x-response-type}}}]

  /**
   * override {{operationId}} with the given handler
   * @return a new implementation of {{classname}}Service[F] with {{operationId}} overridden using the given handler
   */
  final def {{vendorExtensions.x-handlerName}}(handler : ({{{vendorExtensions.x-param-list-typed}}}) => F[{{{vendorExtensions.x-response-type}}}]) : {{classname}}Service[F] = {
    asHandler.copy({{operationId}}Handler = handler)
  }
{{/operation}}
{{/operations}}

  /**
   * @return a Handler implementation of this service
   */
  final def asHandler : {{classname}}Service.Handler[F] = this match {
      case h : {{classname}}Service.Handler[F] => h
      case _ =>
        {{classname}}Service.Handler[F](
        {{#operations}}
          {{#operation}}
              ({{{vendorExtensions.x-param-list}}}) => {{operationId}}({{{vendorExtensions.x-param-list}}}){{^-last}}, {{/-last}}
          {{/operation}}
        {{/operations}}
        )
  }

  /**
   * This function will change the effect type of this service.
   *
   * It's not unlike a typical map operation from A => B, except we're not mapping
   * a type from A to B, but rather from F[A] => G[A] using the 'changeEffect' function.
   *
   * For, this could turn an asynchronous service (one which returns Future[_] types) into
   * a synchronous one (one which returns Try[_] types) by awaiting on the Future.
   *
   * It could change an IO type (like cats effect or ZIO) into an ID[A] which is just:
   * ```
   * type ID[A] => A
   * ```
   *
   * @tparam G the new "polymorphic" effect type
   * @param changeEffect the "natural transformation" which can change one effect type into another
   * @return a new {{classname}}Service service implementation with effect type [G]
   */
  final def mapEffect[G[_]](changeEffect : [A] => F[A] => G[A]) : {{classname}}Service[G] = {
    val self = this

    new {{classname}}Service[G] {
{{#operations}}
    {{#operation}}
        override def {{operationId}}({{{vendorExtensions.x-param-list-typed}}}) : G[{{{vendorExtensions.x-response-type}}}] = changeEffect {
          self.{{operationId}}({{{vendorExtensions.x-param-list}}})
        }
    {{/operation}}
{{/operations}}
    }
  }
}
